import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import vigenere.Kodieren;

public class guiDisplay extends JFrame {

	public class JNumberField extends JTextField {
		JNumberField(int size) {
			super(size);
			addKeyListener(new KeyAdapter() {
				@Override
				public void keyTyped(KeyEvent e) {
					char c = e.getKeyChar();
					if (!(Character.isDigit(c) || (c == KeyEvent.VK_BACK_SPACE) || (c == KeyEvent.VK_DELETE))) {
						e.consume();
					}
				}
			});
		}
	}

	private static final long serialVersionUID = 1L;
	JFileChooser m_fc = new JFileChooser(".");
	JButton open = new JButton("open");
	JButton kasiskiTest = new JButton("Kassiski Test ");
	JButton friedmanTest = new JButton("Friedman Test");
	JButton clearall = new JButton("Clear all");
	JTextArea crypt = new JTextArea(10, 30);
	JTextArea tKasiskiTest = new JTextArea(10, 10);
	JTextArea tFriedmanTest = new JTextArea(10, 10);
	private JTextArea crypte = new JTextArea(20, 20);
	private JTextArea orignalText = new JTextArea(20, 20);
	private JTextField keyt = new JTextField(20);
	private JLabel Key = new JLabel("Key: ");
	private JButton laden = new JButton("Laden");
	private JButton speichern = new JButton("Speichern");
	private JButton cipher = new JButton("verschlüsseln");
	private JButton ladenC = new JButton("Laden");
	private JButton clear = new JButton("Clear all");
	private JButton encryptc = new JButton("enstchlüseln");
	private static String ENCRYPT = "Vigenere-Verschlüsselung";
	private static String CRYPT = "Analyse: Schlüssellänge";
	private static String ANALYSE = "Analyse: Schlüsseltext";
	private JLabel keyLengthLabel = new JLabel("Length: ");
	private JTextField keyLengthField = new JNumberField(20);
	private JLabel keyCharacterLabel = new JLabel("Character: ");
	private JTextField keyCharacterField = new JNumberField(20);
	private JTextArea analyse = new JTextArea(40, 40);
	private JButton analyseButton = new JButton("Analyse");

	public guiDisplay() {
		JTabbedPane tabbedPane = new JTabbedPane();

		// Border paneEdge = BorderFactory.createEmptyBorder(0,10,10,10);
		// Border blackline = BorderFactory.createLineBorder( Color.black);
		// crypt.setBorder(paneEdge);
		tKasiskiTest.setEditable(false);
		analyse.setEditable(false);

		JPanel frame = new JPanel();

		// inserts for Button
		frame.setLayout(new GridBagLayout());
		GridBagConstraints c;

		Insets set = new Insets(10, 10, 10, 10);

		c = new GridBagConstraints();
		c.insets = set;
		c.gridx = 0;
		c.gridy = 0;
		c.anchor = GridBagConstraints.LINE_START;
		frame.add(open, c);
		open.addActionListener(new OpenAction());
		c.gridy = 1;
		frame.add(kasiskiTest, c);
		kasiskiTest.addActionListener(new KasiskiAction());
		c.gridy = 2;
		frame.add(friedmanTest, c);
		friedmanTest.addActionListener(new FriedemannAction());

		c.gridy = 3;
		frame.add(clearall, c);
		clearall.addActionListener(new clearAllAction());

		// insert for button
		c = new GridBagConstraints();
		c.insets = set;
		c.gridx = 1;
		c.gridy = 0;
		c.gridheight = 4;
		c.gridwidth = 4;
		c.weightx = 1.0;
		c.weighty = 1.0;
		c.gridheight = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.LINE_START;
		frame.add(crypt, c);
		frame.add(new JScrollPane(crypt), c);
		frame.setBorder(BorderFactory.createCompoundBorder(BorderFactory
				.createCompoundBorder(BorderFactory
						.createTitledBorder("Verschlüsselte Text"),
						BorderFactory.createEmptyBorder(5, 5, 5, 5)), frame
				.getBorder()));

		// insert for Button
		JPanel panel = new JPanel();
		panel.setLayout(new GridBagLayout());
		GridBagConstraints d = new GridBagConstraints();

		Insets inst = new Insets(10, 10, 10, 10);
		d.insets = inst;
		d.gridx = 0;
		d.gridy = 0;
		// d.gridheight = 2;
		// d.gridwidth =2; // GridBagConstraints.REMAINDER;
		d.gridheight = GridBagConstraints.REMAINDER;
		d.weightx = 1.0;
		d.weighty = 1.0;

		d.fill = GridBagConstraints.BOTH;
		d.anchor = GridBagConstraints.FIRST_LINE_START;
		panel.add(tKasiskiTest, d);
		panel.add(new JScrollPane(tKasiskiTest), d);
		panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory
				.createCompoundBorder(
						BorderFactory.createTitledBorder("Kasiski & Friedmann Test"),
						BorderFactory.createEmptyBorder(5, 5, 5, 5)), panel
				.getBorder()));

		d.gridx = 1;
		panel.add(tFriedmanTest, d);
		panel.add(new JScrollPane(tFriedmanTest), d);
		// panel.setBorder(BorderFactory.createCompoundBorder(
		// BorderFactory.createCompoundBorder(BorderFactory
		// .createTitledBorder("Friedman Test"), BorderFactory
		// .createEmptyBorder(1, 1, 1, 1)), panel
		// .getBorder()));

		// insert two Panel in many Gridlayout
		JPanel generalPanel = new JPanel();
		generalPanel.setLayout(new GridLayout(2, 0));
		generalPanel.add(frame);
		generalPanel.add(panel);
		tabbedPane.addTab(CRYPT, generalPanel);
		tabbedPane.addTab(ANALYSE, haufigkeitsanalyse());
		tabbedPane.addTab(ENCRYPT, krypto());

		//
		this.setContentPane(tabbedPane);
		this.pack();
		this.setTitle("Vigenere Verschlüsselung");
	}

	public JPanel krypto() {
		JPanel generalPanel = new JPanel();
		generalPanel.setLayout(new GridLayout(0, 3));

		JPanel panel1 = new JPanel();
		panel1.setLayout(new GridBagLayout());

		JPanel flow = new JPanel();
		flow.setLayout(new FlowLayout());

		JPanel border = new JPanel();
		border.setLayout(new BorderLayout());

		JPanel panel2 = new JPanel();
		panel2.setLayout(new GridBagLayout());

		GridBagConstraints c;

		Insets inset = new Insets(10, 10, 10, 10);
		c = new GridBagConstraints();
		c.insets = inset;
		c.gridx = 0;
		c.gridy = 0;
		// c.anchor = GridBagConstraints.LINE_START;

		c.gridwidth = GridBagConstraints.REMAINDER;
		c.weightx = 1.0;
		c.weighty = 1.0;

		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		panel1.add(orignalText, c);
		panel1.add(new JScrollPane(orignalText), c);
		panel1.setBorder(BorderFactory.createCompoundBorder(BorderFactory
				.createCompoundBorder(
						BorderFactory.createTitledBorder("Original Text"),
						BorderFactory.createEmptyBorder(5, 5, 5, 5)), panel2
				.getBorder()));

		c.anchor = GridBagConstraints.FIRST_LINE_START;

		c.gridy = 1;
		JPanel flowbuttonOriginal = new JPanel();
		flowbuttonOriginal.setLayout(new FlowLayout());
		flowbuttonOriginal.add(laden);
		laden.addActionListener(new OpenActionOriginalText());
		flowbuttonOriginal.add(cipher);
		cipher.addActionListener(new cipherAction());

		panel1.add(flowbuttonOriginal, c);
		generalPanel.add(panel1);

		flow.add(Key);
		flow.add(keyt);
		border.add(flow, BorderLayout.CENTER);
		border.add(clear, BorderLayout.PAGE_END);
		clear.addActionListener(new clearAction());
		generalPanel.add(border, c);

		GridBagConstraints d;

		Insets insets = new Insets(10, 10, 10, 10);
		d = new GridBagConstraints();
		d.insets = insets;
		d.gridx = 0;
		d.gridy = 0;
		// c.anchor = GridBagConstraints.LINE_START;

		d.gridwidth = GridBagConstraints.REMAINDER;
		d.weightx = 1.0;
		d.weighty = 1.0;

		d.fill = GridBagConstraints.BOTH;
		d.anchor = GridBagConstraints.CENTER;
		panel2.add(crypte, d);
		panel2.add(new JScrollPane(crypte), d);
		panel2.setBorder(BorderFactory.createCompoundBorder(BorderFactory
				.createCompoundBorder(BorderFactory
						.createTitledBorder("Verschlüsselte Text"),
						BorderFactory.createEmptyBorder(5, 5, 5, 5)), panel2
				.getBorder()));

		d.anchor = GridBagConstraints.FIRST_LINE_START;

		d.gridy = 1;
		flowbuttonOriginal = new JPanel();
		flowbuttonOriginal.setLayout(new FlowLayout());
		flowbuttonOriginal.add(ladenC);
		ladenC.addActionListener(new OpenActioncipherText());
		flowbuttonOriginal.add(encryptc);
		encryptc.addActionListener(new decryptAction());
		panel2.add(flowbuttonOriginal, c);
		generalPanel.add(panel2);

		return generalPanel;
	}

	public JPanel haufigkeitsanalyse() {
		JPanel panel = new JPanel();

		panel.add(keyLengthLabel);
		panel.add(keyLengthField);
		keyLengthField.setText("1");
		panel.add(keyCharacterLabel);
		panel.add(keyCharacterField);
		keyCharacterField.setText("1");

		analyse.setLayout(new BorderLayout());
		panel.add(analyse, BorderLayout.NORTH);
		panel.add(new JScrollPane(analyse));
		panel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createCompoundBorder(
						BorderFactory.createTitledBorder("Häufigkeitsanalyse"),
						BorderFactory.createEmptyBorder(5, 5, 5, 5)), panel
						.getBorder()));
		panel.add(analyseButton, BorderLayout.SOUTH);
		analyseButton.addActionListener(new haufigkeitsanalyseAction());
		return panel;
	}

	// ////////////////////////////////////// inner listener class OpenAction
	class OpenAction implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int retval = m_fc.showOpenDialog(guiDisplay.this);
			if (retval == JFileChooser.APPROVE_OPTION) {
				File inFile = m_fc.getSelectedFile();
				try {
					FileReader fr = new FileReader(inFile);
					BufferedReader bufRdr = new BufferedReader(fr);

					String line = null;
					while ((line = bufRdr.readLine()) != null) {
						crypt.append(line);
						crypt.append("\n");
					}
					bufRdr.close();

				} catch (IOException ioex) {
					System.err.println(ioex);
					System.exit(1);
				}
			}
			// if (crypt.getText().isEmpty()){
			// analyseButton.setEnabled(false);
			// }else
			// analyse.setEnabled(true);
			//

		}

	}

	// ///////////////////////////////////innen listenner class kasiski test

	class KasiskiAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {

			int start = 0;
			int ende;
			List<Integer> gemeinsameTeiler = null;

			String mtext = crypt.getText();

			int langetext = mtext.length();
			tKasiskiTest
					.setText("--------------Auswertung ------------\n"
							+ "--------------Kasiski Verfahren------\n "
							+ "*********************************************************\n");

			for (int j = 3; j <= langetext / 2; j++) {
				outerLoop: for (int i = 0; i <= langetext - 3; i++) {
					String sub = mtext.substring(i, (i + j));
					for (char c : sub.toCharArray()) {
						if (c < Kodieren.SMALLEST_CHAR
								|| c > Kodieren.BIGGEST_CHAR) {
							continue outerLoop;
						}
					}
					start = mtext.indexOf(sub, i + j);
					ende = start - i;

					if (start > 0) {
						List<Integer> teiler = teilen(ende);

						if (gemeinsameTeiler == null) {
							gemeinsameTeiler = new ArrayList<Integer>(teiler);
						} else {
							gemeinsameTeiler.retainAll(teiler);
						}

						// ausgabe=false;
						// ende = mtext.indexOf(sub, start +1 );
						tKasiskiTest.append("wort" + "(" + sub.length() + ")"
								+ " :  " + sub + "  Abstand : " + ende
								+ " Position( " + i + " und " + start + " )");
						tKasiskiTest.append("Primzahlzerlegung: (");
						tKasiskiTest.append(stellTeilerDar(teiler));
						tKasiskiTest.append(")");
						tKasiskiTest.append("\n");
					}
				}
				tKasiskiTest.append(stellTeilerDar(gemeinsameTeiler));
			}
		}

		private String stellTeilerDar(List<Integer> teiler) {
			StringBuilder sb = new StringBuilder();
			Iterator<Integer> iterator = teiler.iterator();
			while (iterator.hasNext()) {
				sb.append(iterator.next() + "");
				if (iterator.hasNext()) {
					sb.append("*");
				}
			}
			return sb.toString();
		}

		private List<Integer> teilen(int zahl) {
			List<Integer> teiler = new ArrayList<Integer>();
			int i;
			int max;
			max = (int) Math.floor(Math.sqrt((double) zahl));

			if (zahl <= 1) {
				teiler.add(1);
				return teiler;
			}

			while (zahl % 2 == 0) {
				teiler.add(2);
				zahl /= 2;
			}

			for (i = 3; i <= max; i = i + 2) {
				while (zahl % i == 0) {
					teiler.add(i);
					zahl /= i;
				}
			}
			teiler.add(zahl);
			return teiler;
		}
	}

	// ////////////////////////innere class Friedemann
	// Test////////////////////////

	class FriedemannAction implements ActionListener {

		// Koinzidenzindex für deutsche Texte
		private final double I_d = 0.0762;
		// koinzidenzindex für zufällige Texte
		private final double I_r = 0.0385;

		@Override
		public void actionPerformed(ActionEvent e) {
			tFriedmanTest
					.setText("--------------Auswertung ------------\n"
							+ "--------------Fridemann Test------\n "
							+ "*********************************************************\n");
			tFriedmanTest.append(" Schlüssellang ist ungefähr:"
					+ schluesselang());
			System.out.println(schluesselang());
			System.out.println(analyseKoinzidenzindex(crypt.getText()));

		}

		public float schluesselang() {
			float koinzidenzindex;
			float schluessellange;
			String s = crypt.getText().replaceAll("[^A-Z]", "");

			koinzidenzindex = analyseKoinzidenzindex(s);
			int textlang = s.length();

			schluessellange = (float) (((I_d - I_r) * textlang) / (((textlang - 1) * koinzidenzindex)
					- I_r * textlang + I_d));

			return schluessellange; // Float.toString(schluessellange);

		}

		public float analyseKoinzidenzindex(String s) {
			int[] anzahl = new int[Kodieren.NUMBER_CHARS];
			float koinzidenzindex = 0;
			byte[] text = s.replaceAll("[^A-Z]", "").toUpperCase().getBytes();
			for (byte character : text) {
				anzahl[(character - Kodieren.SMALLEST_CHAR)]++;
			}
			for (int summand : anzahl) {
				koinzidenzindex += summand * (summand - 1);
			}
			koinzidenzindex = koinzidenzindex
					/ (text.length * (text.length - 1));
			return koinzidenzindex;
		}
	}

	// ////////////////////////// class clear all ////////////////

	class clearAllAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			tKasiskiTest.setText("");
			crypt.setText("");
			analyse.setText("");
			tFriedmanTest.setText("");
		}

	}

	// //////////////////////////////////innere class open original text
	// //////////////////////
	class OpenActionOriginalText implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int retval = m_fc.showOpenDialog(guiDisplay.this);
			if (retval == JFileChooser.APPROVE_OPTION) {
				File inFile = m_fc.getSelectedFile();
				try {
					FileReader fr = new FileReader(inFile);
					BufferedReader bufRdr = new BufferedReader(fr);

					String line = null;
					while ((line = bufRdr.readLine()) != null) {
						orignalText.append(line);
						orignalText.append("\n");
					}
					bufRdr.close();

				} catch (IOException ioex) {
					System.err.println(ioex);
					System.exit(1);
				}
			}
		}
	}

	// ///////////////////////////////////////////////////innere class open
	// Action cipher
	class OpenActioncipherText implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int retval = m_fc.showOpenDialog(guiDisplay.this);
			if (retval == JFileChooser.APPROVE_OPTION) {
				File inFile = m_fc.getSelectedFile();
				try {
					FileReader fr = new FileReader(inFile);
					BufferedReader bufRdr = new BufferedReader(fr);

					String line = null;
					while ((line = bufRdr.readLine()) != null) {
						crypte.append(line);
						crypte.append("\n");
					}
					bufRdr.close();

				} catch (IOException ioex) {
					System.err.println(ioex);
					System.exit(1);
				}
			}
		}
	}

	// /////////////////////////////innere class to cipher the original text////
	class cipherAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			String Text = orignalText.getText().toUpperCase();
			String key = keyt.getText().toUpperCase();
			if (key.isEmpty()) {
				JOptionPane.showMessageDialog(keyt, "Schlüssel ist leer ");
			} else {
				Kodieren code = new Kodieren(key);
				StringBuilder output = new StringBuilder();
				for (char c : Text.toCharArray()) {
					output.append(code.encode(c));
				}
				String cipher = output.toString();
				crypte.setText(""); 
				crypte.append(cipher);
				crypte.append("\n");

			}
		}
	}

	// ///////////////////////////////innere class to enschlüsseln the cipher
	// text ///////////////7
	class decryptAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			String Text = crypte.getText().toUpperCase();
			String key = keyt.getText().toUpperCase();
			if (key.isEmpty()) {
				JOptionPane.showMessageDialog(keyt, "Schlüssel ist leer ");
			}else{
			Kodieren code = new Kodieren(key);
			StringBuilder output = new StringBuilder();
			for (char c : Text.toCharArray()) {
				output.append(code.decode(c));
			}
			String decrypt = output.toString();
			orignalText.setText(""); 
			orignalText.append(decrypt);
			crypte.append("\n");
			}

		}
	}

	// ///////////////////////////////////innere class to clear all
	// //////////////////////77

	class clearAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			crypte.setText("");
			keyt.setText("");
			orignalText.setText("");

			// keyt.repaint();
			// orignalText.repaint();
			// crypte.repaint();

		}

	}

	// ////////////////////////////////////////innere class haufigkeitanalyse
	// //////////////77

	class haufigkeitsanalyseAction implements ActionListener {

		private static final int FIRST_CHARACTER = 65;
		private static final int CHARACTER_NUMBER = 26;

		@Override
		public void actionPerformed(ActionEvent e) {
			// TODO: catch number-format-exception and show it appropriately to
			// the user
			int keyLength = Integer.parseInt(keyLengthField.getText());
			int keyCharacter = Integer.parseInt(keyCharacterField.getText());
			analyse.setText("");
			analyse(crypt.getText(), keyLength, keyCharacter);
		}

		public void analyse(String s, int keyLength, int keyCharacter) {
			if (keyCharacter > keyLength) {
				// TODO: Throw an appropriate exception
				return;
			}
			byte[] text = new byte[s.length()];
			int[] anzahl = new int[CHARACTER_NUMBER];
			text = s.toUpperCase().getBytes();
			for (int i = keyCharacter - 1; i < text.length; i += keyLength) {
				byte character = text[i];
				if (character >= FIRST_CHARACTER
						&& character < FIRST_CHARACTER + CHARACTER_NUMBER)
					anzahl[(character - FIRST_CHARACTER)]++; // und jetzt
																// zählen ;-)
			}
			for (int i = 0; i < CHARACTER_NUMBER; i++) {
				analyse.append((char) (i + FIRST_CHARACTER) + ": " + anzahl[i]
						+ "\t" + stern(anzahl[i]));
				analyse.append("\n");
			}

		}

		public String stern(int anzahl) {
			String s = "";
			for (int i = 0; i < anzahl; i++)
				s += "*";
			return s;
		}
	}

	class tabbledAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			analyse.setText("");

		}
	}

}
